//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 1995-2000, The Hypersonic SQL Group.
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Hypersonic SQL Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * on behalf of the Hypersonic SQL Group.
 *
 *
 * For work added by the HSQL Development Group:
 *
 * Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using EffiProzDB.Lib;
using EffiProzDB.RowIO;



namespace EffiProzDB
{


    // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
    // fredt@users 20020920 - path 1.7.1 - refactoring to cut mamory footprint
    // fredt@users 20021205 - path 1.7.2 - enhancements

    /**
     *  Cached table Node implementation.<p>
     *  Only integral references to left, right and parent nodes _in the AVL tree
     *  are held and used as pointers data.<p>
     *
     *  iId is a reference to the Index object that contains this node.<br>
     *  This fields can be eliminated _in the future, by changing the
     *  method signatures to take a Index parameter from Index.java (fredt@users)
     *
     *  New class derived from the Hypersonic code
     *
     * @author Thomas Mueller (Hypersonic SQL Group)
     * @version    1.7.2
     * @since Hypersonic SQL
     */
    class DiskNode : Node
    {

        protected Row rData;
        private int iLeft = NO_POS;
        private int iRight = NO_POS;
        private int iParent = NO_POS;
        private int iId;    // id of Index object for this Node
        public const int SIZE_IN_BYTE = 4 * 4;

        public DiskNode(CachedRow r, RowInputInterface _in,
                 int id)
        {

            iId = id;
            rData = r;
            iBalance = _in.readIntData();
            iLeft = _in.readIntData();

            if (iLeft <= 0)
            {
                iLeft = NO_POS;
            }

            iRight = _in.readIntData();

            if (iRight <= 0)
            {
                iRight = NO_POS;
            }

            iParent = _in.readIntData();

            if (iParent <= 0)
            {
                iParent = NO_POS;
            }

            if (Trace.DOASSERT)
            {

                // fredt - assert not correct - row can be deleted from one index but
                // not yet deleted from other indexes while the process of finding
                // the node is _in progress which may require saving the row
                // to make way for new rows _in the cache and loading it back
                // Trace.doAssert(iBalance != -2);
            }
        }

        public DiskNode(CachedRow r, int id)
        {
            iId = id;
            rData = r;
        }

        public override void delete()
        {
            rData = null;
            iBalance = -2;
        }

        public override int getKey()
        {

            if (rData != null)
            {
                return ((CachedRow)rData).iPos;
            }

            return NO_POS;
        }

        public override Row getRow()
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(rData != null);
            //}

            return rData;
        }

        private Node findNode(int pos)
        {

            Node ret = null;
            Row r = ((CachedRow)rData).getTable().getRow(pos);

            if (r != null)
            {
                ret = r.getNode(iId);
            }

            return ret;
        }

        public override bool isLeft(Node node)
        {

            if (node == null)
            {
                return iLeft == NO_POS;
            }

            return iLeft == ((DiskNode)node).getKey();
        }

        public override bool isRight(Node node)
        {

            if (node == null)
            {
                return iRight == NO_POS;
            }

            return iRight == ((DiskNode)node).getKey();
        }

        public override Node getLeft()
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            if (iLeft == NO_POS)
            {
                return null;
            }

            return findNode(iLeft);
        }

        public override Node getRight()
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            if (iRight == NO_POS)
            {
                return null;
            }

            return findNode(iRight);
        }

        public override Node getParent()
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            if (iParent == NO_POS)
            {
                return null;
            }

            return findNode(iParent);
        }

        public override bool isRoot()
        {
            return iParent == Node.NO_POS;
        }

        public override bool isFromLeft()
        {

            if (this.isRoot())
            {
                return true;
            }

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(getParent() != null);
            //}

            DiskNode parent = (DiskNode)getParent();

            return getKey() == parent.iLeft;
        }

        public override Object[] getData()
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            return rData.getData();
        }

        public override void setParent(Node n)
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            ((CachedRow)rData).setChanged();

            iParent = n == null ? NO_POS
                                : n.getKey();
        }

        public override void setBalance(int b)
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            if (iBalance != b)
            {
                ((CachedRow)rData).setChanged();

                iBalance = b;
            }
        }

        public override void setLeft(Node n)
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            ((CachedRow)rData).setChanged();

            iLeft = n == null ? NO_POS
                              : n.getKey();
        }

        public override void setRight(Node n)
        {

            //if (Trace.DOASSERT)
            //{
            //    Trace.doAssert(iBalance != -2);
            //}

            ((CachedRow)rData).setChanged();

            iRight = n == null ? NO_POS
                               : n.getKey();
        }

      public override bool Equals(Node n)
        {

            /*
                    if (Trace.DOASSERT) {
                        Trace.doAssert(iBalance != -2);

                        if (n != this) {
                            bool test = (getKey() == NO_POS) || (n == null)
                                           || (n.getKey() != getKey());

                            if (test == false) {
                                test = iParent == ((DiskNode) n).iParent
                                       && iLeft == ((DiskNode) n).iLeft
                                       && iRight == ((DiskNode) n).iRight;

                                if (test == false) {
                                    int aA = ((CachedRow) getRow()).iLastAccess;
                                    int bA = ((CachedRow) n.getRow()).iLastAccess;

                                    Trace.doAssert(test,
                                                   "a: " + aA + ", " + iParent + ", "
                                                   + iLeft + ", " + iRight + " b: " + bA
                                                   + ", " + ((DiskNode) n).iParent + ", "
                                                   + ((DiskNode) n).iLeft + ", "
                                                   + ((DiskNode) n).iRight);
                                }
                            }
                        }
                    }
            */
            return this == n
                   || (n != null && getKey() == ((DiskNode)n).getKey());
        }

      public override void write(RowOutputInterface _out)
        {

            if (Trace.DOASSERT)
            {

                // fredt - assert not correct - row can be deleted from one index but
                // not yet deleted from other indexes while the process of finding
                // the node is _in progress which may require saving the row
                // to make way for new rows _in the cache
                // Trace.doAssert(iBalance != -2);
            }

            _out.writeIntData(iBalance);
            _out.writeIntData((iLeft == NO_POS) ? 0
                                               : iLeft);
            _out.writeIntData((iRight == NO_POS) ? 0
                                                : iRight);
            _out.writeIntData((iParent == NO_POS) ? 0
                                                 : iParent);
        }

      public override  Node getUpdatedNode()
        {

            Row row = rData.getUpdatedRow();

            return row == null ? null
                               : row.getNode(iId);
        }

        public void writeTranslate(RowOutputInterface _out, IntLookup lookup)
        {

            _out.writeIntData(iBalance);
            writeTranslatePointer(iLeft, _out, lookup);
            writeTranslatePointer(iRight, _out, lookup);
            writeTranslatePointer(iParent, _out, lookup);
        }

        private void writeTranslatePointer(int pointer, RowOutputInterface _out,
                                           IntLookup lookup)
        {

            int newPointer = 0;

            if (pointer != Node.NO_POS)
            {
                newPointer = lookup.lookupFirstEqual(pointer);
            }

            _out.writeIntData(newPointer);
        }
    }
}
